﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using XNATools.Services.Interfaces;
using XNATools.Services;
using XNATools.Enums;
using Microsoft.Xna.Framework.Input;
using XNATools.Events;
using XNATools.Events.EventArgs;
using XNATools.Exceptions;

namespace XNATools.Managers
{
    /// <summary>
    /// Class for inputs managment
    /// </summary>
    public sealed class InputManager : ServiceBase,IInputManagerService
    {
        #region Fields

        private InputHandle _Handle = InputHandle.All;
        private IKeyboardService _KeyboardService;
        private IMouseService _MouseService;

        /// <summary>
        /// <see cref="XNATools.Services.Interfaces.IInputManagerService.KeyboardAction"/>
        /// </summary>
        public event KeyboardActionEventHandler KeyboardAction;
        /// <summary>
        /// <see cref="XNATools.Services.Interfaces.IInputManagerService.MouseMove"/>
        /// </summary>
        public event MouseActionEventHandler MouseMove;
        /// <summary>
        /// <see cref="XNATools.Services.Interfaces.IInputManagerService.MouseButtonAction"/>
        /// </summary>
        public event MouseActionEventHandler MouseButtonAction;

        #endregion

        #region cTors()

        /// <summary>
        /// Create an InputManager
        /// </summary>
        /// <param name="game">for this specified game</param>
        /// <exception cref="XNATools.Exceptions.ServiceMissingException" />
        public InputManager(Game game)
            : base(game)
        {
            UpdateOrder = int.MinValue;            
        }

        /// <summary>
        /// Create an InputManager
        /// </summary>
        /// <param name="game">for this specified game</param>
        /// <param name="handle">Handle type</param>
        /// <exception cref="XNATools.Exceptions.ServiceMissingException" />
        public InputManager(Game game, InputHandle handle)
            : this(game)
        {
            _Handle = handle;
        }

        #endregion

        #region Methods

        /// <summary>
        /// <see cref="Microsoft.Xna.Framework.GameComponent.Initialize"/>
        /// </summary>
        public override void Initialize()
        {
            if (IsHandled(InputHandle.Keyboard))
            {
                _KeyboardService = ServiceManager.Get<IKeyboardService>();
                if (_KeyboardService == null)
                {
                    throw new ServiceMissingException("IKeyboardService");
                }
            }
            if (IsHandled(InputHandle.Mouse))
            {
                _MouseService = ServiceManager.Get<IMouseService>();
                if (_MouseService == null)
                {
                    throw new ServiceMissingException("IMouseService");
                }
            }
            base.Initialize();
        }

        /// <summary>
        /// <see cref="Microsoft.Xna.Framework.GameComponent.Update"/>
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            if (IsHandled(InputHandle.Keyboard) && _KeyboardService.StateHasChanged)
            {
                OnKeyboardAction(_KeyboardService.PressedKeys);
            }
            if (IsHandled(InputHandle.Mouse) && _MouseService.StateHasChanged)
            {
                if (_MouseService.Offset != Vector2.Zero)
                {
                    OnMouseMove();
                }
                if (_MouseService.IsButtonJustPressed(MouseButton.Left))
                {
                    OnMouseButtonAction(MouseButton.Left);
                }
                if (_MouseService.IsButtonJustPressed(MouseButton.Right))
                {
                    OnMouseButtonAction(MouseButton.Right);
                }
                if (_MouseService.IsButtonJustPressed(MouseButton.Middle))
                {
                    OnMouseButtonAction(MouseButton.Middle);
                }
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// <see cref="XNATools.Services.ServiceBase.IsThisServiceExistInManager"/>
        /// </summary>
        /// <returns></returns>
        protected override bool IsThisServiceExistInManager()
        {
            return ServiceManager.Exists<IInputManagerService>(this);
        }

        /// <summary>
        /// <see cref="XNATools.Services.ServiceBase.RegisterService"/>
        /// </summary>
        protected override void RegisterService()
        {
            ServiceManager.Add<IInputManagerService>(this);
        }

        /// <summary>
        /// Determine if specified handle type is handled
        /// </summary>
        /// <param name="handle">Handle type</param>
        /// <returns>True if handled; otherwise, false</returns>
        private bool IsHandled(InputHandle handle)
        {
            return (_Handle & handle) == handle;
        }

        /// <summary>
        /// Raise KeyboardAction event
        /// </summary>
        /// <param name="pressedKeys">Key who are currently in down state</param>
        internal void OnKeyboardAction(Keys[] pressedKeys)
        {
            if (KeyboardAction != null)
                KeyboardAction(this, new KeyboardActionEventArgs(pressedKeys));
        }

        /// <summary>
        /// Raise mouse move event
        /// </summary>
        internal void OnMouseMove()
        {
            if (MouseMove != null)
                MouseMove(this, new MouseActionEventArgs());
        }

        /// <summary>
        /// Raise mouse button action event
        /// </summary>
        internal void OnMouseButtonAction(MouseButton button)
        {
            if (MouseButtonAction != null)
                MouseButtonAction(this, new MouseActionEventArgs(button));
        }

        #endregion

        #region IInputManagerService Members

        /// <summary>
        /// <see cref="XNATools.Services.Interfaces.IInputManagerService.KeyboardService"/>
        /// </summary>
        public IKeyboardService KeyboardService
        {
            get 
            {
                return _KeyboardService;
            }
        }

        /// <summary>
        /// <see cref="XNATools.Services.Interfaces.IInputManagerService.MouseService"/>
        /// </summary>
        public IMouseService MouseService
        {
            get 
            {
                return _MouseService;
            }
        }

        #endregion
    }
}
